package org.activequant.tradesystem.ib.selectorstrategy;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;

import org.activequant.core.domainmodel.InstrumentSpecification;
import org.activequant.core.domainmodel.marketscanner.ScanCriteria;
import org.activequant.tradesystem.ib.InstrumentStats;
import org.activequant.tradesystem.ib.MarketScannerFactory;
/**
 * TopScannersSelector implements ISelectorStrategy<br/>
 * Holds the following associated variable:<br/>
 * <ul>
 * <li>selectedScanCriterias(List&lt;ScanCriteria&gt;)</li>
 * </ul>
 * Its used to find the top 10 most often seen ScannerCriteria's from given possibleCandidates(Map&lt;InstrumentSpecification,InstrumentStats&gt;)
 * @author Dimitar
 *
 */
public class TopScannersSelector implements ISelectorStrategy{
	/**
	 * private List&lt;ScanCriteria&gt; selectedScanCriterias;
	 */
	private List<ScanCriteria> selectedScanCriterias;
	/**
	 * Returns the given possibleCandidates(Map&lt;InstrumentSpecification,InstrumentStats&gt;) unchanged (for chaining). The given marketScanHistory(MarketScanHistory) is not used either.
	 * Looks at the given possibleCandidates(Map&lt;InstrumentSpecification,InstrumentStats&gt;) and finds out how many times was each ScanCriteria seen. 
	 * Sorts these values in a descending order and sets the associated selectedScanCriterias(List&lt;ScanCriteria&gt;) with the top 10 (IB_SIMULTANEOUS_SCAN_LIMIT)
	 */
	public Map<InstrumentSpecification, InstrumentStats> getOpportunities(MarketScannerFactory marketScanHistory, Map<InstrumentSpecification, InstrumentStats> possibleCandidates) {
		Map<ScanCriteria,Integer> scannerFrequency=new HashMap<ScanCriteria,Integer>();
		for(Entry<InstrumentSpecification,InstrumentStats> candidate : possibleCandidates.entrySet()){
			//InstrumentSpecification spec=candidate.getKey();
			//System.out.println("instrument= "+spec.getSymbol()+ " "+ spec.getSecurityType());
			InstrumentStats stats= candidate.getValue();
			//contractDetailsSource.requestInstrumentSpecificationDetails(spec, stats.listener);
			for(Entry<ScanCriteria,Integer> entry: stats.getCriterias().entrySet()){
				ScanCriteria scanCriteria=entry.getKey();				
				Integer count= scannerFrequency.get(scanCriteria);
				if(count==null){
					count=1;
				}else{
					count++;
				}
				scannerFrequency.put(scanCriteria, count);
				//Integer ranking= entry.getValue();
				//System.out.println("scanCriteria= "+scanCriteria.getScanCode()+ " #"+ ranking);
			}
		}
		SortedMap<ScanCriteria, Integer> sortedScannerFrequency = new TreeMap<ScanCriteria, Integer>(new FrequencyComparator(scannerFrequency));
        //System.out.println("scannerFrequency.size= "+scannerFrequency.size());
        sortedScannerFrequency.putAll(scannerFrequency);
        //System.out.println("sorted_map.size= "+sortedScannerFrequency.size());
        selectedScanCriterias=new ArrayList<ScanCriteria>();
        for (ScanCriteria key : sortedScannerFrequency.keySet()) {
            System.out.println("key/value: " + key.getScanCode() + "/"+sortedScannerFrequency.get(key));
        	selectedScanCriterias.add(key);
        	if(selectedScanCriterias.size()>=MarketScannerFactory.IB_SIMULTANEOUS_SCAN_LIMIT){
        		break;
        	}
        }
		return possibleCandidates;
	}

	
	/**
	 * A Comparator used to sort a Map&lt;ScanCriteria,Integer&gt; by their frequencies(Integer) in descending order<br/>
	 * FrequencyComparator implements Comparator&lt;ScanCriteria&gt;<br/>
	 * Holds the following associated variables:
	 * <ul>
	 * <li>base(Map&lt;ScanCriteria,Integer&gt;)</li>
	 * </ul>
	 * @author Dimitar
	 *
	 */
	class FrequencyComparator implements Comparator<ScanCriteria> {
		/**
		 * Map&lt;ScanCriteria,Integer&gt; base;
		 */
		Map<ScanCriteria,Integer> base;
		/**
		 * constructs a FrequencyComparator(implements Comparator&lt;ScanCriteria&gt;) using the given base(Map&lt;ScanCriteria,Integer&gt;) to set its associated
		 * base(Map&lt;ScanCriteria,Integer&gt;)
		 * @param base
		 */
		public FrequencyComparator(Map<ScanCriteria, Integer> base) {
			this.base = base;
		}
		public int compare(ScanCriteria o1, ScanCriteria o2) {
			Integer frequency1=base.get(o1)!=null?base.get(o1):0;
			Integer frequency2=base.get(o2)!=null?base.get(o2):0;
			if(frequency1 < frequency2) {
				return 1;
			} else if(frequency1 == frequency2) {
				return 0;
			} else {
				return -1;
			}
		}
	}


	/**
	 * returns the associated selectedScanCriterias(List&lt;ScanCriteria&gt;)
	 * @return
	 */
	public List<ScanCriteria> getSelectedScanCriterias() {
		return selectedScanCriterias;
	}	
}
